using System;
using System.Collections.Generic;
using System.Text;
using Barrage;
using Barrage.Global;
using SlimDX;
using SlimDX.Direct3D9;
using Barrage.Global.Vertex;

namespace Barrage.GUI.Meshes
{
    /// <summary>
    /// Mesh that renders a rectangle with some decoration on its border. It holds information of the size of each border to mantain the decoration indepently of size.
    /// </summary>
    public class BorderedRectangle: Barrage.Meshes.GuiMesh
    {
        // Left-Top-Right-Bottom
        float[] coordBorderSize=new float[4];
        float[] uvBorderSize=new float[4];
        bool dirty = false;
        Vector2 size;

        /// <summary>
        /// Mesh size
        /// </summary>
        public Vector2 Size { get { return size; } set { size = value; dirty = true; } }
        
        Vector2 offset=Vector2.Zero;

        /// <summary>
        /// Mesh offset
        /// </summary>
        public Vector2 Offset { get { return offset; } set { offset = value; dirty = true; } }


        VertexBuffer rectangleBuffer = null;
        static IndexBuffer indexBuffer = null;

        static void initializeIndexBuffer(){
            indexBuffer = new IndexBuffer(Engine.Device, 4 * 54, Usage.WriteOnly, Pool.Managed, false);
            DataStream stream = indexBuffer.Lock(0, 0, LockFlags.None);
            int[] indices = new int[54]{
                0,1,2,  2,3,1,  1,3,5,  1,5,4,  4,5,6,  6,5,7,
                7,5,8,  7,8,9,  9,8,11, 9,11,10,    8,12,13,
                8,13,11,    12,15,14,   12,14,13,   3,2,15,
                3,15,12,    5,3,12, 5,12,8
            };
            stream.WriteRange(indices);
            indexBuffer.Unlock();
            Engine.RegisterDisposeHandler(new EngineDisposeHandler(DisposeIndexBuffer));
        }

        static void DisposeIndexBuffer()
        {
            if (indexBuffer != null)
                indexBuffer.Dispose();
            indexBuffer = null;
            NLog.LogManager.GetCurrentClassLogger().Debug("Disposing index buffer");
            
        }

        

        void initializeBuffer()
        {
            rectangleBuffer = new VertexBuffer(Engine.Device, VertexPositionUV.SizeBytes * 16, Usage.WriteOnly, VertexPositionUV.Format, Pool.Managed);
            fillBuffer();
        }

        void fillBuffer()
        {
            DataStream stream = rectangleBuffer.Lock(0, 0, LockFlags.None);
            VertexPositionUV[] vertices = generateVertices();
            stream.WriteRange(vertices);
            rectangleBuffer.Unlock();

            dirty = false;
            
        }

        VertexPositionUV[] generateVertices(){
            VertexPositionUV[] vertices = new VertexPositionUV[16];
            float L=-size.X/2.0f + offset.X;
            float l = -size.X / 2.0f + offset.X + coordBorderSize[0];
            float R = size.X / 2.0f + offset.X;
            float r = size.X / 2.0f + offset.X - coordBorderSize[2];
            float B = -size.Y / 2.0f + offset.Y;
            float b = -size.Y / 2.0f + offset.Y + coordBorderSize[3];
            float T = size.Y / 2.0f + offset.Y;
            float t = size.Y / 2.0f + offset.Y - coordBorderSize[1];
            vertices[0].Position = new Vector3(L, T, 0); vertices[0].UV = new Vector2(0, 0);
            vertices[1].Position = new Vector3(l,T,0); vertices[1].UV=new Vector2(uvBorderSize[0],0);
            vertices[2].Position = new Vector3(L,t,0); vertices[2].UV=new Vector2(0,uvBorderSize[1]);
            vertices[3].Position = new Vector3(l,t,0); vertices[3].UV=new Vector2(uvBorderSize[0],uvBorderSize[1]);
            
            vertices[4].Position = new Vector3(r,T,0); vertices[4].UV = new Vector2(1-uvBorderSize[2], 0);
            vertices[5].Position = new Vector3(r,t,0); vertices[5].UV = new Vector2(1-uvBorderSize[2], uvBorderSize[1]);
            vertices[6].Position = new Vector3(R,T,0); vertices[6].UV = new Vector2(1, 0);
            vertices[7].Position = new Vector3(R,t,0); vertices[7].UV = new Vector2(1, uvBorderSize[1]);
            
            vertices[8].Position = new Vector3(r,b,0); vertices[8].UV = new Vector2(1-uvBorderSize[2], 1-uvBorderSize[3]);
            vertices[9].Position = new Vector3(R,b,0); vertices[9].UV = new Vector2(1, 1-uvBorderSize[3]);
            
            vertices[10].Position = new Vector3(R,B,0); vertices[10].UV = new Vector2(1, 1);
            vertices[11].Position = new Vector3(r,B,0); vertices[11].UV = new Vector2(1-uvBorderSize[2], 1);
            
            vertices[12].Position = new Vector3(l,b,0); vertices[12].UV = new Vector2(uvBorderSize[0], 1-uvBorderSize[3]);
            vertices[13].Position = new Vector3(l,B,0); vertices[13].UV = new Vector2(uvBorderSize[0], 1);

            vertices[14].Position = new Vector3(L,B,0); vertices[14].UV = new Vector2(0,1);
            vertices[15].Position = new Vector3(L,b,0); vertices[15].UV = new Vector2(0, 1-uvBorderSize[3]);

            return vertices;
            
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="texture">Texture used to render the rectangle</param>
        /// <param name="size">Initial size</param>
        /// <param name="coordBorder">Size of each border (Left-Top-Right-Bottom) in pixels</param>
        /// <param name="uvBorder">UV size of each border (Left-Top-Right-Bottom) in the texture</param>
        public BorderedRectangle(Texture texture,Vector2 size,float[] coordBorder,float[]uvBorder){
            this.size = size;
            this.coordBorderSize = coordBorder;
            this.uvBorderSize = uvBorder;
            initializeBuffer();
            if (indexBuffer == null)
                initializeIndexBuffer();
            Materials[0].Texture = texture;
        }

        /// <summary>
        /// \internal Renders the rectangle
        /// </summary>
        /// <param name="item">Item that's rendering this mesh</param>
        public override void Render(Item item)
        {
            shader.BeginRender(item, materials[0]);
            //D3DEngine.Device.SetRenderState(RenderState.CullMode, Cull.None);
            if (dirty) { fillBuffer(); }
            Engine.Device.VertexFormat = VertexPositionUV.Format;
            Engine.Device.Indices = indexBuffer;
            Engine.Device.SetStreamSource(0, rectangleBuffer, 0, VertexPositionUV.SizeBytes);
            int passNumber = shader.PassNumber(materials[0]);
            for (int j = 0; j < passNumber; j++)
            {
                shader.BeginPass(j);
                Engine.Device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 16, 0, 18);
                shader.EndPass();
            } 
            shader.EndRender();
            
        }

        /// <summary>
        /// \internal Clear resources
        /// </summary>
        public override void Dispose()
        {
            rectangleBuffer.Dispose();
            rectangleBuffer = null;
            base.Dispose();
        }

    }
}
